package opc;

import com.google.common.collect.ImmutableList;
import opc.certificate.CerLoad;
import opc.certificate.PKCS12LoadImp;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.eclipse.milo.opcua.stack.client.UaTcpStackClient;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.Stack;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;

/**
 * //https://blog.csdn.net/u010695169/article/details/115759926?utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~default-6.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~default-6.no_search_link
 * opc配置
 * 1、安全策略运行无证书模式。
 * 2、开启匿名访问模式。
 */
public class RunDemo {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    //定义OPC UA服务器的IP地址和端口
    private static String EndPointUrl = "opc.tcp://127.0.0.1:49320";
    private static final CompletableFuture<OpcUaClient> future = new CompletableFuture<>();

    public static void main(String args[]) throws Exception {
        //定义证书文件路径
        File cerfile = new File("D:\\devCode\\gitWorkSpace\\springboot-opc-ua-utgard\\src\\main\\resources\\example-client.pfx");
        CerLoad cerLoad = new PKCS12LoadImp();    //加载pfx证书文件的实例
//        Runner runner = new ReadExample();   //Runner对象
//        CerLoad cerLoad = new DerPemLoadImp();    //加载der和pem证书文件的实例
        X509Certificate certificate = cerLoad.getCer(cerfile);  //获取证书对象
        KeyPair keyPair = cerLoad.getKeyPair(cerfile);          //获取密钥对 对象

        //搜索OPC节点
        EndpointDescription[] endpointDescriptions = null;
        System.out.println("开始OPC连接...");
        try {
            endpointDescriptions = UaTcpStackClient.getEndpoints(EndPointUrl).get();
            System.out.println(endpointDescriptions[1]);
        } catch (Throwable e) {
            System.out.println("获取端点失败");
            return;
        }

        EndpointDescription endpointDescription = endpointDescriptions[1];

        //创建OpcUaClientConfig 配置对象
        OpcUaClientConfig config = OpcUaClientConfig.builder()
                .setApplicationName(LocalizedText.english("demo"))
                .setApplicationUri("urn:eclipse:milo:examples:client")
                .setCertificate(certificate)
                .setKeyPair(keyPair)
                .setEndpoint(endpointDescription)
//                .setIdentityProvider(new AnonymousProvider() )
                .setIdentityProvider(new UsernameProvider("Administrator", "123456"))
                .setRequestTimeout(uint(5000))
                .build();

        //利用OpcUaClientConfig创建OPC UA客户端
        OpcUaClient opcClient = new OpcUaClient(config);
        future.whenCompleteAsync((c, ex) -> {
            if (ex != null) {
                System.out.println("连接OPC UA服务错误: {}" + ex.getMessage());
            }
            // 关闭OPC UA客户端
            try {
                opcClient.disconnect().get();
                Stack.releaseSharedResources();
            } catch (InterruptedException | ExecutionException e) {
                System.out.println("OPC UA服务关闭错误: {}" + e.getMessage());
            }
        });
        //读取
//        run(opcClient,future);
        //订阅
        createSubscription(opcClient);
//        try {
//            opcClient.connect().get();

//            List<org.eclipse.milo.opcua.sdk.client.api.nodes.Node> nodes = opcClient.getAddressSpace().browse(Identifiers.RootFolder).get();
//            for(org.eclipse.milo.opcua.sdk.client.api.nodes.Node node: nodes){
//                System.out.println("Node= " + node.getBrowseName().get().getName());
//                Object value = node.readDescription().get().getValue().getValue();
//                System.out.println(value);
//            }
//            //创建连接
//            //写入值
//            int v = 1;
//
//            NodeId nodeId = new NodeId(3,"\"TAG1\"");
//            DataValue dataValue = new DataValue(new Variant(v),null,null);
//
//            StatusCode statusCode = opcClient.writeValue(nodeId,dataValue).get();
//
//            System.out.println(statusCode.isGood());
//
//            DataValue value = opcClient.readValue(0.0, TimestampsToReturn.Both, nodeId).get();
//
//            System.out.println((Integer)value.getValue().getValue());
////            runner.Run(opcClient);
//        } catch (Exception e) {
//            System.out.println("发生异常: " + e);
//        }
        future.complete(opcClient);
    }

    /**
     * 读取值
     * @param client
     * @param future
     * @throws Exception
     */
    public static void run(OpcUaClient client, CompletableFuture<OpcUaClient> future) throws Exception {
        // 同步建立连接
        client.connect().get();

        // 异步读取数据
        readTagData(client).thenAccept(values -> {
            DataValue nodeId_Tag1 = values.get(0);
            DataValue nodeId_Tag2 = values.get(1);
            System.out.println("#########Tag1=" + nodeId_Tag1.getValue().getValue());
            System.out.println("#########Tag2=" + nodeId_Tag2.getValue().getValue());
            future.complete(client);
        });
    }

    /**
     * 读取标签点的数据
     */
    private static CompletableFuture<List<DataValue>> readTagData(OpcUaClient client) {
        NodeId nodeId_Tag1 = new NodeId(2, "tongdao2.shebei1.TAG1");
        NodeId nodeId_Tag2 = new NodeId(2, "tongdao2.shebei1.TAG2");

        List<NodeId> nodeIds = ImmutableList.of(nodeId_Tag1, nodeId_Tag2);
        return client.readValues(0.0, TimestampsToReturn.Both, nodeIds);
    }

    /**
     * 订阅
     * @param client
     * @throws Exception
     */
    public static void createSubscription(OpcUaClient client) throws Exception {
        // 同步建立连接
        client.connect().get();
        //创建监控项请求
        //创建发布间隔1000ms的订阅对象
        UaSubscription subscription = client.getSubscriptionManager().createSubscription(1000.0).get();
        // 你所需要订阅的key
        List<String> key = new ArrayList<>();
        key.add("tongdao2.shebei1.TAG1");
        key.add("tongdao2.shebei1.TAG2");

        while (true){
            for (int i = 0; i < key.size(); i++) {
                String node = key.get(i);
                Thread.sleep(1000);
                //创建订阅的变量
                NodeId nodeId = new NodeId(2, node);
                ReadValueId readValueId = new ReadValueId(nodeId, AttributeId.Value.uid(), null, null);
                //创建监控的参数
                MonitoringParameters parameters = new MonitoringParameters(
                        uint(1 + i),  // 为了保证唯一性，否则key值一致
                        1000.0,     // sampling interval
                        null,       // filter, null means use default
                        uint(10),   // queue size
                        true        // discard oldest
                );

                MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(readValueId, MonitoringMode.Reporting, parameters);
                List<MonitoredItemCreateRequest> requests = new ArrayList<>();
                requests.add(request);
                //创建监控项，并且注册变量值改变时候的回调函数。
                List<UaMonitoredItem> items = subscription.createMonitoredItems(
                        TimestampsToReturn.Both,
                        requests,
                        (item, id) -> {
                            item.setValueConsumer((is, value) -> {
                                String nodeName = item.getReadValueId().getNodeId().getIdentifier().toString();
                                String nodeValue = value.getValue().getValue().toString();
                                Date javaDate = value.getServerTime().getJavaDate();
                                System.out.println("订阅成功, item1 :" + item.getReadValueId().getNodeId().toString());
                                System.out.println("nodeName==="+nodeName +"  nodeValue====="+nodeValue);
                                System.out.println("javaDate====== :" + javaDate);
                            });
                        }).get();
            }
        }


    }
//
//————————————————
//    版权声明：本文为CSDN博主「郝小祺」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
//    原文链接：https://blog.csdn.net/weixin_43400608/article/details/116202941
}
